function forEach(obj, fn) {
  if (obj === null || typeof obj === "undefined") {
    return;
  }

  if (typeof obj !== "object") {
    obj = [obj];
  }

  if (Array.isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    for (let key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

function bind(fn, thisArg) {
  // 这里用fn.apply来实现函数this绑定，注意需要返回的也是一个函数，所以这里包一层
  return function wrap() {
    // 把多个参数，转成数组的形式
    let args = new Array(arguments.length);
    for (let i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }

    // args是一个数组，apply调用方式
    return fn.apply(thisArg, args);
  };
}

// 实现继承，b->a 如果是函数要绑定this指向
function extend(a, b, thisArg) {
  forEach(b, function (val, key) {
    if (thisArg && typeof val === "function") {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

// 合并对象，返回一个新的对象
function merge() {
  let result = {};

  function assignValue(val, key) {
    // result里的是一个对象，并且val也是一个对象，深拷贝
    if (typeof result[key] === "object" && typeof val === "object") {
      result[key] = merge(result[key], val);
    } else if (typeof result[key] === "object") {
      result[key] = merge({}, val);
    } else {
      result[key] = val;
    }
  }

  // 有多个参数要合并，依次循环遍历，合并到result
  for (let i = 0; i < arguments.length; i++) {
    forEach(arguments[i], assignValue);
  }

  return result;
}

function isObject(val) {
  return val !== null && typeof val === "object";
}

export default {
  forEach,
  bind,
  extend,
  merge,
  isObject,
};
